package com.program.facesigninsystem.face;

import com.arcsoft.face.*;
import com.arcsoft.face.enums.CompareModel;
import com.arcsoft.face.enums.DetectMode;
import com.arcsoft.face.enums.DetectOrient;
import com.arcsoft.face.enums.ErrorInfo;
import com.arcsoft.face.toolkit.ImageInfo;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;

/**
 * @author Mapler
 */
public class ArcSoftFace {
    private static Logger log = Logger.getLogger(ArcSoftFace.class.toString());
    final private static String APP_ID_FACE = "Aq1NsjmJnG7GqJnr7pb1QQ8WaLVWYRURDQ7YQkjjJra4";
    final private static String SDK_KEY = "DF2UYiFC5ZdpTQ8EKARcqNgqquUbB42cdbgj5xxpq5rC";
    private  FaceEngine faceEngine = new FaceEngine("C:\\FaceSystem\\lib\\WIN64");

    public ArcSoftFace(){
        init();
    }

    /***
     * 激活人脸识别引擎
     * @return 激活结果
     */
        public int activeOnline(){
            int errorCode = faceEngine.activeOnline(APP_ID_FACE, SDK_KEY);
            if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())
            {
                System.out.println("引擎激活失败");
            }
            return errorCode;
        }

    /***
     * 初始化人脸识别引擎
     */
    private void init(){
            EngineConfiguration engineConfiguration = new EngineConfiguration();
            //引擎配置
            engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
            engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
            engineConfiguration.setDetectFaceMaxNum(50);
            engineConfiguration.setDetectFaceScaleVal(16);
            //功能配置
            FunctionConfiguration functionConfiguration = new FunctionConfiguration();
            functionConfiguration.setSupportAge(false);
            functionConfiguration.setSupportFace3dAngle(false);
            functionConfiguration.setSupportFaceDetect(true);
            functionConfiguration.setSupportFaceRecognition(true);
            functionConfiguration.setSupportGender(false);
            functionConfiguration.setSupportLiveness(true);
            functionConfiguration.setSupportIRLiveness(true);
            engineConfiguration.setFunctionConfiguration(functionConfiguration);
            //初始化引擎
            int errorCode;
            errorCode = faceEngine.init(engineConfiguration);
            if (errorCode != ErrorInfo.MOK.getValue()) {
                System.out.println("初始化引擎失败");
            }
    }

    /***
     * 人脸识别
     * @param file 图片文件
     * @return 获取到的人脸list
     */
    public List<FaceInfo> detectFaces(File file){
        List<FaceInfo> faceInfoList = new ArrayList<>();
        try {
            ImageInfo imageInfo = getRGBData(file);
            FunctionConfiguration configuration = new FunctionConfiguration();
            configuration.setSupportAge(false);
            configuration.setSupportFace3dAngle(false);
            configuration.setSupportGender(false);
            configuration.setSupportLiveness(true);
            faceEngine.detectFaces(imageInfo.getImageData(),
                    imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(),
                    faceInfoList);
            if (faceInfoList.size() > 1) {
                return faceInfoList;
            }
            faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(),
                    faceInfoList, configuration);
            return faceInfoList;
        }catch (Exception e) {
            log.log(Level.SEVERE,"Error parsing date",e);
            return faceInfoList;
        }
    }

    /***
     * 提取人脸特征
     * @param file 图片文件
     * @param faceInfoList 人脸list
     * @return 提取到的特征数据
     */
    public byte[] extractFaceFeature(File file,List<FaceInfo> faceInfoList){
        try {
            ImageInfo imageInfo = getRGBData(file);
            FaceFeature faceFeature = new FaceFeature();
            faceEngine.extractFaceFeature(imageInfo.getImageData(),
                    imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(),
                    faceInfoList.get(0), faceFeature);
            byte[] bytes = faceFeature.getFeatureData();
            //noinspection ResultOfMethodCallIgnored
            file.delete();
            return bytes;
        }catch(Exception e){
            return null;
        }
    }
    /***
     * 批量提取人脸特征
     * @param file 图片文件
     * @param faceInfoList 人脸list
     * @return 批量提取的特征数据
     */
    public List<byte[]> extractFaceFeatures(File file,List<FaceInfo> faceInfoList){
        try {
            List<byte[]> bytesList = new ArrayList<>();
            ImageInfo imageInfo;
            for(int t =0;t<1000;t++){
                if(file.canRead()) {
                    imageInfo = getRGBData(file);
                    for (FaceInfo aFaceInfoList : faceInfoList) {
                        byte[] bytes;
                        FaceFeature faceFeature = new FaceFeature();
                        faceEngine.extractFaceFeature(imageInfo.getImageData(),
                                imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(),
                                aFaceInfoList, faceFeature);
                        bytes = faceFeature.getFeatureData();
                        bytesList.add(bytes);
                    }
                    break;
                }
            }
            //noinspection ResultOfMethodCallIgnored
            file.delete();
            return bytesList;
        }catch(Exception e){
            return null;
        }
    }

    /***
     * 对比人脸
     * @param targetFaceFeature 目标特征数据
     * @param sourceFaceFeature 源特征数据
     * @return 对比相似度
     */
    public float compareFaceFeature(FaceFeature targetFaceFeature,FaceFeature sourceFaceFeature){
        int errorCode;
        FaceSimilar faceSimilar = new FaceSimilar();
        errorCode=faceEngine.compareFaceFeature(targetFaceFeature,sourceFaceFeature, CompareModel.LIFE_PHOTO,faceSimilar);
        if(errorCode == 0) {
            return faceSimilar.getScore();
        }
        return 0;
    }

    /**
     * 多人脸对比
     * @param targetMapList 目标list
     * @param sourceMapList 源list
     * @param similar 指定相似度
     * @return 识别成功的结果
     */
    public List<String> compareFaceFeatures(List<Map<String,Object>> targetMapList, List<Map<String,Object>> sourceMapList,double similar){
        int errorCode;
        FaceSimilar faceSimilar = new FaceSimilar();
        Map targetMap;
        Map sourceMap;
        List<String> result = new ArrayList<>();
        byte[] bytes;
        for (Map<String, Object> aTargetMapList : targetMapList) {
            targetMap = aTargetMapList;
            bytes = (byte[]) targetMap.get("faceFeature");
            FaceFeature targetFaceFeature = new FaceFeature(bytes);
            for (Map<String, Object> aSourceMapList : sourceMapList) {
                sourceMap = aSourceMapList;
                bytes = (byte[]) sourceMap.get("faceFeature");
                FaceFeature sourceFaceFeature = new FaceFeature(bytes);
                errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, CompareModel.LIFE_PHOTO, faceSimilar);
                if (errorCode == 0 && faceSimilar.getScore() >= similar) {
                    result.add((String) sourceMap.get("uid"));
                }
            }
        }
        return result;
    }

    /***
     * 活体检测
     * @return 返回活体检测结果
     */
    public int getLiveness(){
        int errorCode;
        List<LivenessInfo> livenessInfoList = new ArrayList<>();
        errorCode = faceEngine.getLiveness(livenessInfoList);
        if(errorCode == 0) {
            try {
                if(livenessInfoList.size() == 0) {
                    return 5;
                }
                else {
                    return livenessInfoList.get(0).getLiveness();
                }
            }catch (Exception e){
                log.log(Level.SEVERE,"Error parsing date",e);
                return 5;
            }
        }
        return 5;
    }

    public void destroyEngine() {
        faceEngine.unInit();
    }
}
